﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using DefCore.Tools;
using DefCore.Core;
using Krypton.Lights;
using Krypton;
using Ivory;
using DefCore.Interfaces;

namespace DefCore.Mapping
{
    public class CellMap : MapBase<CellBase>, IRenderManager
    {
        //Fields         
        public RenderTarget2D Buffer
        {
            get;
            private set;
        }

        public List<Light2D> LightsInScene;
        public List<ShadowHull> HullsInScene;

        public static bool RenderStructureFrames { get; set; }
        public int RenderOrder
        {
            get { return 1; }
        }
       
        //ctor
        public CellMap(CellBase[,] matrix)
            : base(matrix)
        {
            CellMap.RenderStructureFrames = false;
        }

        //Methods
        public IEnumerable<CellBase> GetAll()
        {
            for (int y = 0; y < MapHeight; y++)
                for (int x = 0; x < MapWidth; x++)
                    yield return this[x, y];
        }
        public IEnumerable<CellBase> FilterBy(Func<CellBase, bool> func)
        {
            return this.GetAll().Where(func);
        }
        public IEnumerable<CellBase> FilterByVisible()
        {
            //Calculate the Minimum tiles shown
            int minX, minY;
            minX = (int)(Objects.GameCamera.CameraTopLeft.X / Globals.TileSize.X);
            minY = (int)(Objects.GameCamera.CameraTopLeft.Y / Globals.TileSize.Y);
            minX = minX >= 1 ? minX - 1 : 0;
            minY = minY >= 1 ? minY - 1 : 0;

            //Calculate the Maximum Tiles shown
            int maxX, maxY;
            maxX = (int)((Objects.GameCamera.CameraTopLeft.X + Objects.GameCamera.CameraViewport.X) / Globals.TileSize.X);
            maxY = (int)((Objects.GameCamera.CameraTopLeft.Y + Objects.GameCamera.CameraViewport.Y) / Globals.TileSize.Y);
            maxX = maxX < MapWidth ? maxX + 1 : MapWidth;
            maxY = maxY < MapWidth ? maxY + 1 : MapHeight;

            for (int y = minY; y < maxY; y++)
                for (int x = minX; x < maxX; x++)
                    yield return this[x, y];
        }
        
        public void SolveCellMap(InputControl input, GameTime time)
        {
            foreach (CellBase cell in GetAll())
            {
                int count = 0;
                foreach (Point p in Globals.Directions)
                {
                    Point newMap = new Point(cell.MapPosition.MapPosition.X + p.X, cell.MapPosition.MapPosition.Y + p.Y);
                    if (!Exists(newMap.X, newMap.Y))
                    {
                        count++;
                        continue;
                    }

                    switch (count)
                    {
                        case 0: cell.NorthNeighboughOpen = this[newMap.X, newMap.Y].CellBody == null; break;
                        case 1: cell.EastNeighboughOpen = this[newMap.X, newMap.Y].CellBody == null; break;
                        case 2: cell.SouthNeighboughOpen = this[newMap.X, newMap.Y].CellBody == null; break;
                        case 3: cell.WestNeighboughOpen = this[newMap.X, newMap.Y].CellBody == null; break; 

                        default: throw new Exception("Unexpected Error: 0x6yhd");
                    }

                    count++;
                }

                cell.Solve(input, time);
            }
        }        
        public void LoadComponents()
        {
            foreach (CellBase cell in GetAll())
                cell.Load();
        }

        public void CreateBuffer(GameTime time)
        {
            LightsInScene = new List<Light2D>(); 
            Buffer = new RenderTarget2D(Objects.GameObject.GraphicsDevice,
                (int)Objects.GameCamera.CameraViewport.X, (int)Objects.GameCamera.CameraViewport.Y,
                false, SurfaceFormat.Color, DepthFormat.None, 0, RenderTargetUsage.PreserveContents);

            using (SpriteBatch mapBatch = new SpriteBatch(Buffer.GraphicsDevice))
            {
                mapBatch.GraphicsDevice.SetRenderTarget(Buffer);
                mapBatch.GraphicsDevice.Clear(Color.Black);
                mapBatch.Begin();

                foreach (CellBase cell in FilterByVisible())
                {
                    cell.Render(mapBatch, null);
                    if(cell.CellLight != null)
                        LightsInScene.Add(cell.CellLight);
                }

                mapBatch.End();
                mapBatch.GraphicsDevice.SetRenderTarget(null);
            }

            HullsInScene = new List<ShadowHull>();
            foreach (CellBase cell in FilterByVisible())
            {
                if (!cell.IsWalkable && cell.CellShadowHull != null)
                    HullsInScene.Add(cell.CellShadowHull);
            }
        }
    }
}
